package com.skymxc.processor;

import com.skymxc.annotation.OnClick;

import java.io.IOException;
import java.io.OutputStream;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

/**
 * <p>
 *
 * </p>
 *
 * @author 孟祥超
 * <p>
 * date: 2021/8/14  8:05 下午
 */
public class MyAnnotationProcessor extends AbstractProcessor {

    /**
     *
     * @param annotations 需要处理的注解
     * @param roundEnv 上一轮处理器的信息
     * @return true 标识已经处理了这些元素，后续不会再传过来；false 表示没有处理，后续还会再传过来。
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        // processingEnv 是 AbstractProcessor 中的一个 ProcessingEnvironment 类型的属性，标识处理器环境，可以用来获取一些常见对象。
        //消息输出，可以在 run 窗口看到编译时输出消息，类似我们在 logcat 窗口里看到的日志
        Messager messager = processingEnv.getMessager();
        //输出日志，Diagnostic.Kind.NOTE 标识级别，ERROR 的话 不会停止运行，需要自己停止
        messager.printMessage(Diagnostic.Kind.NOTE,"我是在 MyAnnotationProcessor 输出的 NOTE");
        // ERROR  级别，输出这个级别表示又错误，编译就会失败，但是代码不会停止，需要自己 return 停止运行。
//        messager.printMessage(Diagnostic.Kind.ERROR,"我是在 MyAnnotationProcessor 输出的 ERROR");


        // Elements 对元素进行操作的工具类，封装了很多操作元素的方法
        Elements elementUtils = processingEnv.getElementUtils();

        //返回一些用于对类型进行操作的实用方法的实现。Types:对类型进行操作的实用方法实现
        Types typeUtils = processingEnv.getTypeUtils();

        //获取给注解处理器的参数选项，没有的话为 null
        Map<String, String> options = processingEnv.getOptions();

        if (null!=options){
            Set<String> keySet = options.keySet();
            for (String key:keySet){
                messager.printMessage(Diagnostic.Kind.NOTE,"MyAnnotationProcessor.options->"+key+":"+options.get(key));
            }
        }

        //返回用于创建新的源，类或辅助文件的文件管理器。Filer：文件管理器，可用于创建源文件，资源文件，读取资源文件等操作。
        Filer filer = processingEnv.getFiler();

        // 注解处理器可能会被回调多次，因为一旦有新的文件被创建，就会再重新走一次流程，
        if (roundEnv.processingOver()){
            //已经处理完了
           messager.printMessage(Diagnostic.Kind.NOTE,"MyAnnotationProcessor 已经处理结束了");
            return true;
        }

        if (annotations.isEmpty()){
            messager.printMessage(Diagnostic.Kind.NOTE,"MyAnnotationProcessor annotations.isEmpty()");
            return true;
        }

        //返回true如果在上一轮处理中出现错误; 返回false其他。
        boolean errorRaised = roundEnv.errorRaised();
        messager.printMessage(Diagnostic.Kind.NOTE,"MyAnnotationProcessor roundEnv.errorRaised():"+errorRaised);


        //返回使用给定注解的元素，只返回本轮注解处理器中的元素
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(OnClick.class);

        // Element 表示程序元素，如程序包，类或方法。
        for (Element element:elements){
            String packageName = elementUtils.getPackageOf(element).getQualifiedName().toString();
            messager.printMessage(Diagnostic.Kind.NOTE,"MyAnnotationProcessor OnClick.class->element:"+element.getSimpleName()+" ,包名是："+packageName);
            //ElementKind :枚举类，表示元素类型
            if (element.getKind() == ElementKind.METHOD){ //方法
                ExecutableElement executableElement = (ExecutableElement) element;
                //包含它的元素，也就是类
                Element enclosingElement = executableElement.getEnclosingElement();
                if (enclosingElement.getKind()==ElementKind.CLASS){
                    TypeElement elementType = (TypeElement) enclosingElement;
                    messager.printMessage(Diagnostic.Kind.NOTE,"MyAnnotationProcessor OnClick.class->"+element.getSimpleName()+".getEnclosingElement 是一个类："+elementType.getQualifiedName());
                }
            }
        }

        String code = "package com.skymxc.example;\n" +
                "\n" +
                "public final class BuildConfig_2 {\n" +
                "  public static final boolean DEBUG = false;\n" +
                "  public static final String APPLICATION_ID = \"com.skymxc.example\";\n" +
                "  public static final String BUILD_TYPE = \"release\";\n" +
                "  public static final int VERSION_CODE = 1;\n" +
                "  public static final String VERSION_NAME = \"1.0\";\n" +
                "}\n";
        OutputStream outputStream =null;
        try {
            //创建一个源文件,放到源目录
            JavaFileObject sourceFile = filer.createSourceFile("BuildConfig_2");
             outputStream = sourceFile.openOutputStream();
             outputStream.write(code.getBytes());
             outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (null!=outputStream){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return true; //已经处理了
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> set = new LinkedHashSet<>();
        set.add(OnClick.class.getCanonicalName());
        return set;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
}
